// 基于视差映射的云层
Shader "PJD/Scerne/CloudParallax"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color("Color",Color) = (1,1,1,1)
        _Alpha("Alpha",Range(0,1)) = 0.5
        _Height("Displacement Amount",range(0,1)) = 0.15
        _HeightAmount("Turbulence Amount",range(0,2)) = 1
        _HeightTileSpeed("Turbulence Tile&Speed",Vector) = (1.0,1.0,0.05,0.0)  // 用来修改 uv 流动变化
        _LightIntensity ("Ambient Intensity", Range(0,2)) = 1.0
        [Toggle] _UseFixedLight("Use Fixed Light", Int) = 1  // 是否启动加光照 可以考虑使用range 做插值
        _FixedLightDir("Fixed Light Direction", Vector) = (0.981, 0.122, -0.148, 0.0)  //自定义 假的光照向量
        _LinearStep("LinearStep",Range(5,30)) = 12
    }
    SubShader
    {
        Tags 
        {
            "IgnoreProjector"="True"
            "Queue"="Transparent"
            "RenderType"="Transparent"
        }
        LOD 100

        //Blend SrcAlpha OneMinusSrcAlpha
        Cull Off

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"
            #include "AutoLight.cginc"
            #include "Lighting.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
                float4 color : COLOR;
            };

            struct v2f
            {
                float4 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 normalDir : TEXCOORD1;
                float3 viewDir : TEXCOORD2;
                float4 posWorld : TEXCOORD3;
                float4 color : TEXCOORD4;
                UNITY_FOG_COORDS(5)
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            half _Height;
            float4 _HeightTileSpeed;
            half _HeightAmount;
            half4 _Color;
            half _LightIntensity;
            half _Alpha;

            half _DirectLightAmount;
            half4 _LightingColor;
            half4 _FixedLightDir;
            half _UseFixedLight;
            half _LinearStep;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                // 水平移动可以增加随机性扰动
                o.uv.xy = TRANSFORM_TEX(v.uv, _MainTex) + frac(_Time.y * _HeightTileSpeed.zw);
                // 竖直方向也可以稍微再改改
                o.uv.zw = v.uv * _HeightTileSpeed.xy;
                o.posWorld = mul(unity_ObjectToWorld,v.vertex);
                o.normalDir = UnityObjectToWorldNormal(v.normal);

                // TANGENT_SPACE_ROTATION 省略了矩阵的构建
                float3 binormal = cross(v.normal, v.tangent.xyz) * v.tangent.w;
                float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);
                //TANGENT_SPACE_ROTATION;
                // 切线空间下
                o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex));
                o.color = v.color;
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 viewRay = normalize(i.viewDir * -1);
                viewRay.z = abs(viewRay.z) + 0.2;

                // _Height 控制云的凹凸程度
                viewRay.xy *= _Height;

                // shadeP、shadeP2 用于记录迭代的状态
				// 水平方向上uv 流动  shadeP的xy是采样uv，z是当前深度
                float3 shadeP = float3(i.uv.xy,0);
                // 竖直方向上偏移 shadeP2的xy是扰动uv,z没有用到
                float3 shadeP2 = float3(i.uv.zw,0);

                // 实际相对应的是陡峭的次数 数值越大 高低变化越平滑(lioffset 越小 while 中计算次数越多 )
                float linearStep = _LinearStep;

                //取纹理的 a 通道作为高度
                float4 mainTex = tex2D(_MainTex,shadeP2.xy);
                // _HeightAmount 调整云的高度 越小越矮
                float height = mainTex.a * _HeightAmount;

                // uv 偏移
                float3 offset = viewRay / (viewRay.z * linearStep);
                // 翻转高度  （项目用的深度图其实是高度图 所以 取的是一个反向的值）
                float d = 1.0 - tex2Dlod(_MainTex,fixed4(shadeP.xy,0,0)).a * height;

                //float d = 1.0 - tex2D(_MainTex,shadeP.xy).a * height;

                // 记录前后值
                float3 prev_d = d;
                float3 prev_shadeP = shadeP;

                // 先线性查找，最后直接对最后两次查找做线性插值
				//偏移直到 d 小于0结束
                while(d > shadeP.z)
                {
                    prev_shadeP = shadeP;
                    shadeP += offset;
                    prev_d = d;
                    // 使用 tex2Dlod 而不使用 tex2D 的原因 是 while 会无法展开报错
                    d = 1.0 - tex2Dlod(_MainTex,fixed4(shadeP.xy,0,0)).a * height;
                    //d = 1.0 - tex2D(_MainTex,shadeP.xy).a * height;
                }

                // 对最后两次做线性插值
                float d1 = d - shadeP.z;
                float d2 = prev_d - prev_shadeP.z;
                float w = d1 / (d1 - d2);
                shadeP = lerp(shadeP,prev_shadeP,w);

                half4 col = tex2D(_MainTex,shadeP.xy) * mainTex * _Color;
                half alpha = lerp(col.a, 1.0,_Alpha) * i.color.r;

                // lambert 光照计算

                float normal = normalize(i.normalDir);
                // _FixedLightDir 这个是自己定义的一个光照的向量
                half3 lightDir1 = normalize(_FixedLightDir.xyz);
                // 真实的摄像机到顶点的光照向量
                // UnityWorldSpaceLightDir 中做了计算
                half3 lightDir2 = UnityWorldSpaceLightDir(i.posWorld);

                half3 lightDir = lerp(lightDir2,lightDir1,_UseFixedLight);

                // NdotL 现在不对，晚点再看看
                float NdotL = max(0,dot(normal,lightDir2));
                half3 lightColor = _LightColor0.rgb;
                fixed3 finalColor = col.rgb * (NdotL * lightColor + _LightIntensity);

                return half4(finalColor.rgb,alpha);
            }
            ENDCG
        }
    }
}
